#include "GeneralEdit.h"
#include "Editor.h"
#include "Windows/GxWinClipBoard.h"
#include "Basic/StringBuilder.h"

using namespace GxWinGUI;

namespace FinalRendering
{
	namespace Editor
	{
		using namespace GxLibBasic;

		void CopyObjectCommand::PerformAction(Scene * scene)
		{
			StringBuilder sb(2097152);
			for (int i=0; i<selection.Count(); i++)
			{
				int id = selection[i].ID;
				if (selection[i].Type == Selection::otBrush)
				{
					scene->Brushes[id]->Serialize(sb, 0);
				}
				else
				{
					scene->Entities[id]->Serialize(sb, 0);
				}
			}
			ClipBoard::SetText(sb.GetStrPtr());
		}

		void CopyObjectCommand::UndoAction(Scene * scene)
		{
		}

		PasteObjectCommand::PasteObjectCommand()
		{
			performed = false;
		}

		void PasteObjectCommand::PerformAction(Scene * scene)
		{
			WorldEditor->CancelSelection();
			if (!performed)
			{
				String str = ClipBoard::GetText();
				if (!str.GetLength())
					return;
				LexStream stream;
				if (!scene->Parse(str, stream))
					return;
				TokenNode * node = stream.Begin();
				try
				{
					while (node && node->Data.TypeID != (int)Lex::RBrace)
					{
						String str = node->Data.Str;
						if (str == L"brush")
						{
							RefPtr<Brush> b = new Brush();
							b->UnSerialize(node);
							brushes.Add(b);
						}
						else if (str == L"entity")
						{
							RefPtr<Entity> e = Entity::UnSerialize(node);
							if (e)
							{
								entities.Add(e);
							}
						}
						else
							throw 0;
					}
				}
				catch (...)
				{
					brushes.Clear();
					entities.Clear();
					return;
				}
				Mapping<int,int> gmap;
				for (int i=0; i<brushes.Count(); i++)
				{
					int gid = brushes[i]->GroupID;
					if (gid != -1)
					{
						int rs;
						bool succ = gmap.Lookup(gid,rs);
						if (!succ)
						{
							int ngid = scene->AllocateGroupID();
							gmap.Add(gid, ngid);
							brushes[i]->GroupID = ngid;
						}
						else
							brushes[i]->GroupID = rs;
					}
					scene->Brushes.Add(brushes[i]);
				}
				for (int i=0; i<entities.Count(); i++)
				{
					int gid = entities[i]->GroupID;
					if (gid != -1)
					{
						int rs;
						bool succ = gmap.Lookup(gid,rs);
						if (!succ)
						{
							int ngid = scene->AllocateGroupID();
							gmap.Add(gid, ngid);
							entities[i]->GroupID = ngid;
						}
						else
							entities[i]->GroupID = rs;
					}
					entities[i]->Selected = true;
					entities[i]->Name = scene->AllocEntityName(entities[i]->Name);
					scene->Entities.Add(entities[i]);
				}
				performed = true;
			}
			else
			{
				for (int i=0; i<brushes.Count(); i++)
					brushes[i]->Selected = true;
				scene->Brushes.Add(brushes);
				for (int i=0; i<entities.Count(); i++)
					entities[i]->Selected = true;
				scene->Entities.Add(entities);
			}
			WorldEditor->SelectionChanged();
			scene->Modified = true;
		}

		void PasteObjectCommand::UndoAction(Scene * scene)
		{
			scene->Brushes.Delete(scene->Brushes.Count()-brushes.Count(), brushes.Count());
			scene->Entities.Delete(scene->Entities.Count()-entities.Count(), entities.Count());
		}

		DeleteObjectCommand::DeleteObjectCommand()
		{
			update = true;
		}

		void DeleteObjectCommand::PerformAction(FinalRendering::Editor::Scene *scene)
		{
			for (int i=0; i<brushID.Count(); i++)
			{
				int id = brushID[i]-i;
				brushes.Add(scene->Brushes[id]);
				scene->Brushes.Delete(id);
			}
			for (int i=0; i<entityID.Count(); i++)
			{
				int id = entityID[i]-i;
				entities.Add(scene->Entities[id]);
				scene->Entities.Delete(id);
			}
			if (update)
			{
				WorldEditor->SelectionChanged();
				scene->Modified = true;
			}
		}

		void DeleteObjectCommand::UndoAction(FinalRendering::Editor::Scene *scene)
		{
			for (int i=0; i<brushID.Count(); i++)
			{
				scene->Brushes.Insert(brushID[i],brushes[i]);
			}
			for (int i=0; i<entityID.Count(); i++)
			{
				scene->Entities.Insert(entityID[i],entities[i]);
			}
			if (update)
			{
				WorldEditor->SelectionChanged();
				scene->Modified = true;
			}
		}

		void GroupCommand::PerformAction(FinalRendering::Editor::Scene * Scene)
		{
			int gid;
			if (!group)
			{
				gid = Scene->AllocateGroupID();
				Group *ngroup = new Group;
				ngroup->Name = String(L"Group") + String(gid);
				ngroup->ID = gid;
				ngroup->ParentID = -1;
				Scene->Groups.Add(ngroup);
				group = Scene->Groups.End();
			}
			else
			{
				gid = group->ID;
				Group *ngroup = new Group;
				ngroup->Name = group->Name;
				ngroup->ID = group->ID;
				ngroup->ParentID = group->ParentID;
				Scene->Groups.Add(ngroup);
				group = Scene->Groups.End();
			}
			oriGroupID.SetSize(selection.Count());
			
			for (int i=0; i<selection.Count(); i++)
			{
				Selection::ObjectType type;
				int id;
				type = selection[i].Type;
				id = selection[i].ID;
				if (type == Selection::otBrush)
				{
					oriGroupID[i] = Scene->Brushes[id]->GroupID;
					if (Scene->Brushes[id]->GroupID != -1)
					{
						int idx = Scene->GetTopLevelGroupIndex(Scene->Brushes[id]->GroupID);
						if (idx!=-1 && Scene->Groups[idx]->ID != gid)
							Scene->Groups[idx]->ParentID = gid;
					}
					else
						Scene->Brushes[id]->GroupID = gid;
				}
				else
				{
					oriGroupID[i] = Scene->Entities[id]->GroupID;
					if (Scene->Entities[id]->GroupID != -1)
					{
						int idx = Scene->GetTopLevelGroupIndex(Scene->Entities[id]->GroupID);
						if (idx!=-1)
							Scene->Groups[idx]->ParentID = gid;
					}
					else
						Scene->Entities[id]->GroupID = gid;
				}
			}
			
			Scene->Modified = true;
		}

		void GroupCommand::UndoAction(Scene * Scene)
		{
			for (int i=0; i<selection.Count(); i++)
			{
				Selection::ObjectType type;
				int id;
				type = selection[i].Type;
				id = selection[i].ID;
				if (type == Selection::otBrush)
				{
					Scene->Brushes[id]->GroupID = oriGroupID[i];
				}
				else
				{
					Scene->Entities[id]->GroupID = oriGroupID[i];
				}
			}
			for (int i=0; i<Scene->Groups.Count(); i++)
			{
				if (Scene->Groups[i]->ParentID == group->ID)
					Scene->Groups[i]->ParentID = -1;
			}
			int idx = Scene->FindGroupID(group->ID);
			Scene->Groups.Delete(idx);
			oriGroupID.Clear();
			Scene->Modified = true;
		}

		void UngroupCommand::PerformAction(FinalRendering::Editor::Scene *Scene)
		{
			OrderedArray<int> gids;
			for (int i=0; i<Selection.Count(); i++)
			{
				Selection::ObjectType type;
				int id;
				type = Selection[i].Type;
				id = Selection[i].ID;
				int cgid = Scene->GetTopLevelGroupIndex(type==Selection::otBrush?Scene->Brushes[id]->GroupID:Scene->Entities[id]->GroupID);
				if (cgid != -1)
				{
					cgid = Scene->Groups[cgid]->ID;
					if (cgid != -1)
						gids.Combine(cgid);
				}
			}
			
			for (int i=0; i<Scene->Brushes.Count();i++)
			{
				int id = Scene->Brushes[i]->GroupID;
				if (gids.Exists(id))
				{
					Modification mod;
					mod.sel.ID = i;
					mod.sel.Type = Selection::otBrush;
					mod.oriGroupID = Scene->Brushes[i]->GroupID;
					modifications.Add(mod);
					Scene->Brushes[i]->GroupID = -1;
				}
			}
			for (int i=0; i<Scene->Entities.Count();i++)
			{
				bool found = false;
				int id = Scene->Entities[i]->GroupID;
				if (gids.Exists(id))
				{
					Modification mod;
					mod.sel.ID = i;
					mod.sel.Type = Selection::otEntity;
					mod.oriGroupID = Scene->Entities[i]->GroupID;
					modifications.Add(mod);
					Scene->Entities[i]->GroupID = -1;
				}
			}
			for (int i=0; i<gids.Count(); i++)
			{
				int fid = Scene->FindGroupID(gids[i]);
				if (fid != -1)
				{
					DeletedGroup * grp = new DeletedGroup();
					grp->group = Scene->Groups[fid];
					removedGroups.Add(grp);
					for (int j=0; j<Scene->Groups.Count(); j++)
					{
						if (Scene->Groups[j]->ParentID == gids[i])
						{
							grp->childGroups.Add(Scene->Groups[j]->ID);
							Scene->Groups[j]->ParentID = -1;
						}
					}
				}
			}
			for (int i=0; i<gids.Count(); i++)
			{
				int fid = Scene->FindGroupID(gids[i]);
				if (fid != -1)
					Scene->Groups.Delete(fid);
			}
			Scene->Modified = true;
		}

		void UngroupCommand::UndoAction(Scene * Scene)
		{
			for (int i=0; i<removedGroups.Count(); i++)
			{
				
				Scene->Groups.Add(removedGroups[i]->group);
			}
			for (int i=0; i<removedGroups.Count(); i++)
			{
				for (int j=0; j<removedGroups[i]->childGroups.Count(); j++)
				{
					int id = Scene->FindGroupID(removedGroups[i]->childGroups[j]);
					Scene->Groups[id]->ParentID = removedGroups[i]->group->ID;
				}
			}
			for (int i=0; i<modifications.Count(); i++)
			{
				Selection::ObjectType type = modifications[i].sel.Type;
				int id = modifications[i].sel.ID;
				if (type == Selection::otBrush)
				{
					Scene->Brushes[id]->GroupID = modifications[i].oriGroupID;
				}
				else
				{
					Scene->Entities[id]->GroupID = modifications[i].oriGroupID;
				}
			}
			removedGroups.Clear(true);
			modifications.Clear();
			Scene->Modified = true;
		}

		void AddBrushCommand::PerformAction(Scene * scene)
		{
			scene->Brushes.Add(brush);
		}

		void AddBrushCommand::UndoAction(Scene * scene)
		{
			scene->Brushes.Delete(scene->Brushes.Count()-1);
		}
	}
}